/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.bingoogolapple.badgeview;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.media.image.common.Position;

import java.security.SecureRandom;
import java.util.Random;

/**
 * BGAExplosionAnimator
 *
 * @since 2021-04-21
 */
public class BGAExplosionAnimator extends AnimatorValue implements AnimatorValue.ValueUpdateListener {

    // 线性加速插值器
    private static final int DEFAULT_INTERPOLATOR = Animator.CurveType.ACCELERATE;
    private static final float END_VALUE = 1.4f;
    private static final int REFRESH_RATIO = 3;
    private double X;
    private double Y;
    private double V;
    private double W;

    private Particle[] mParticles;
    private Paint mPaint;
    private Component mDragBadgeView;
    private Rect mRect;

    private float currentValue = 0;

    /**
     * 初始化动画
     *
     * @param dragBadgeView
     * @param rect
     * @param Color
     */
    public BGAExplosionAnimator(Component dragBadgeView, Rect rect, int Color) {
        mDragBadgeView = dragBadgeView;
        setDuration(mDragBadgeView.getLocationOnScreen()[1]);
        setCurveTypeInternal(DEFAULT_INTERPOLATOR);
        setValueUpdateListener(this);

        X = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 5);
        Y = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 20);
        V = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 2);
        W = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 1);

        mPaint = new Paint();

        mRect = rect;
        int partLen = 15;
        mParticles = new Particle[partLen * partLen];
        SecureRandom random = new SecureRandom();
        int w = 50;
        int h = 50;
        for (int i = 0; i < partLen; i++) {
            for (int j = 0; j < partLen; j++) {
                Position position = new Position();
                position.posX = (j + 1) * w;
                position.posY = (i + 1) * h;
                mParticles[(i * partLen) + j] = generateParticle(Color, random);
            }
        }
        setValueUpdateListener(this::onUpdate);
    }

    /**
     * generateParticle
     *
     * @param color
     * @param random
     * @return Particle
     */
    private Particle generateParticle(int color, Random random) {
        Particle particle = new Particle();
        particle.color = color;
        particle.radius = (float) V;
        if (random.nextDouble() < 0.2f) {
            particle.baseRadius = (float) (V + ((X - V) * random.nextDouble()));
        } else {
            particle.baseRadius = (float) (W + ((V - W) * random.nextDouble()));
        }
        double nextDouble = random.nextDouble();
        particle.top = (float) (mRect.getHeight() * ((0.18f * random.nextDouble()) + 0.2f));
        particle.top = nextDouble < 0.2f ? particle.top : (float) (particle.top + ((particle.top * 0.2f) * random.nextDouble()));
        particle.bottom = (float) ((mRect.getHeight() * (random.nextDouble() - 0.5f)) * 1.8f);

        float particleFloat = 0f;
        if (nextDouble < 0.2f) {
            particleFloat = particle.bottom;
        } else {
            particleFloat = (float) nextDouble;
        }

        // nextDouble < 0.2f ? particle.bottom : nextDouble < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
        double f = particleFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
        particle.bottom = (float) f;
        particle.mag = 4.0f * particle.top / particle.bottom;
        particle.neg = (-particle.mag) / particle.bottom;
        f = (float) (mRect.getCenterX() + (Y * (random.nextDouble() - 0.5f)));

        double mSub = NumberUtil.div(mRect.getWidth() , 2);

        particle.baseCx = (float) NumberUtil.add (f,mSub);
        particle.cx = particle.baseCx;
        f = (float) (mRect.getCenterY() + (Y * (random.nextDouble() - 0.5f)));
        particle.baseCy = (float) f;
        particle.cy = (float) f;
        particle.life = (float) (END_VALUE / 10 * random.nextDouble());
        particle.overflow = (float) (0.4f * random.nextDouble());
        particle.alpha = 1f;
        return particle;
    }

    /**
     * draw
     *
     * @param canvas
     */
    public void draw(Canvas canvas) {

        if (!isRunning()) {
            return;
        }
        for (Particle particle : mParticles) {
            particle.advance(currentValue);
            if (particle.alpha > 0f) {
                mPaint.setColor(new Color(particle.color));
                mPaint.setAlpha((Color.alpha(particle.color) * particle.alpha) / 255);
                canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);
            }
        }
        postInvalidate();
    }

    /**
     * start
     */
    @Override
    public void start() {
        super.start();
    }

    /**
     * 只刷新徽章附近的区域
     */
    private void postInvalidate() {
        mDragBadgeView.invalidate();
    }

    /**
     * onUpdate
     *
     * @param animatorValue
     * @param v
     */
    @Override
    public void onUpdate(AnimatorValue animatorValue, float v) {
        currentValue = v * END_VALUE;
        mDragBadgeView.invalidate();
    }

    /**
     * Particle
     */
    private class Particle {
        float alpha;
        int color;
        float cx;
        float cy;
        float radius;
        float baseCx;
        float baseCy;
        float baseRadius;
        float top;
        float bottom;
        float mag;
        float neg;
        double life;
        double overflow;

        /**
         * advance
         *
         * @param factor
         */
        public void advance(double factor) {
            double f = 0f;
            LogUtil.error("advance", "advance" + factor);
            double normalization = (factor / END_VALUE);
            LogUtil.error("Particle", "normalization" + normalization + " life" + life + " normalization" + normalization + " overflow" + overflow);
            if (normalization < life || normalization > 1f - overflow) {
                alpha = 0f;
                return;
            }
            normalization = (normalization - life) / (1f - life - overflow);
            double f2 = (normalization * END_VALUE);
            if (normalization >= 0.7f) {
                f = (float) ((normalization - 0.7f) / 0.3f);
            }
            alpha = (float) (1f - f);
            f = bottom * f2;
            cx = (float) (baseCx + f);
            cy = (float) ((float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag);
            radius = (float) (V + (baseRadius - V) * f2);
        }
    }
}
